package clue

import (
	"context"
	"fmt"
	"git.mycaigou.com/gfyx/common/tenant_db"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/constants"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/dto/aggregation"
	"gorm.io/gorm"
)

type clueRepo struct {
	tenant_db.TenantDB
}

func NewClueRepo() ClueRepositoryIface {
	return &clueRepo{}
}

func (c *clueRepo) ClueQueryList(ctx context.Context, condition map[string]interface{}, pageInfo *utils.PageInfo) (int64, []*GfyxCluePo, error) {
	db := c.GetTenantDB(ctx).Table(GfyxCluePo{}.TableName()+" as a").
		Joins("left join "+GfyxClueTrackerPo{}.TableName()+" as b on a.id = b.clue_id and b.is_deleted = ?", constants.IsDeletedFalse).
		Where("a.is_deleted = ?", constants.IsDeletedFalse).Group("a.id")

	if len(condition) > 0 {
		if keyword, ok := condition["keyword"]; ok {
			k := fmt.Sprintf("%%%s%%", keyword.(string))
			db = db.Where("a.contact_name like ? or a.contact_mobile like ? or a.company_name like ?", k, k, k)
			delete(condition, "keyword")
		}
		if areaCodes, ok := condition["area_code_list"]; ok {
			db = db.Where("a.province_code in (?) or a.city_code in (?)", areaCodes, areaCodes)
			delete(condition, "area_code_list")
		}
		if statusList, ok := condition["status_list"]; ok {
			db = db.Where("a.status in (?)", statusList)
			delete(condition, "status_list")
		}
		if chargeUserIds, ok := condition["charge_user_ids"]; ok {
			db = db.Where("b.user_id in (?)", chargeUserIds)
			delete(condition, "charge_user_ids")
		}
		if assigned, ok := condition["assigned"]; ok {
			if v, ok2 := assigned.(int32); ok2 {
				if v == 1 {
					//已分配的线索，即存在跟进人的线索
					db = db.Where("b.id is not null")
				} else {
					//未分配的线索，即不存在跟进人的线索
					db = db.Where("b.id is null")
				}
			}
			delete(condition, "assigned")
		}

		if len(condition) > 0 {
			db = db.Where(condition)
		}
	}
	var count int64
	var queryList []*GfyxCluePo
	err := db.Select("count(DISTINCT(a.id))").Count(&count).Error
	if err != nil {
		return 0, nil, err
	}

	if pageInfo != nil && pageInfo.Page > 0 && pageInfo.PageSize > 0 {
		db = db.Offset((pageInfo.Page - 1) * pageInfo.PageSize).Limit(pageInfo.PageSize)
	}
	if err = db.Order("a.id desc").Select("a.*").Find(&queryList).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return 0, nil, nil
		}
		return 0, nil, err
	}
	return count, queryList, nil
}

func (c *clueRepo) ClueUpdate(ctx context.Context, condition, updates map[string]interface{}) error {
	return c.GetTenantDB(ctx).Table(GfyxCluePo{}.TableName()).Where(condition).Updates(updates).Error
}

func (c *clueRepo) ClueDetail(ctx context.Context, clueId int64) (*GfyxCluePo, error) {
	cluePo := GfyxCluePo{}

	err := c.GetTenantDB(ctx).Table(cluePo.TableName()).
		Where("id = ? and is_deleted = ?", clueId, constants.IsDeletedFalse).
		First(&cluePo).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &cluePo, nil
}

func (c *clueRepo) ClueSave(ctx context.Context, po *GfyxCluePo) (*GfyxCluePo, error) {
	err := c.GetTenantDB(ctx).Table(po.TableName()).Create(po).Error
	return po, err
}

func (c *clueRepo) ClueCount(ctx context.Context, condition map[string]interface{}) (int64, error) {
	var cnt int64
	err := c.GetTenantDB(ctx).Table(GfyxCluePo{}.TableName() + " as a").Where(condition).Count(&cnt).Error
	if err != nil {
		return 0, err
	}
	return cnt, nil
}

func (c *clueRepo) ClueUseRank(ctx context.Context, beginDate, endDate string, pageInfo *utils.PageInfo) ([]*aggregation.UseRankItem, error) {
	var list []*aggregation.UseRankItem
	db := c.GetTenantDB(ctx).Table(GfyxCluePo{}.TableName()).Select("created_by as user_id, count(1) as total").
		Where("created_on between ? and ? and created_by <> 0 and is_deleted = ?", beginDate, endDate, constants.IsDeletedFalse)

	if pageInfo != nil && pageInfo.Page > 0 && pageInfo.PageSize > 0 {
		db = db.Offset((pageInfo.Page - 1) * pageInfo.PageSize).Limit(pageInfo.PageSize)
	}
	err := db.Group("created_by").Order("total desc").Find(&list).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, err
	}
	return list, nil
}

func (c *clueRepo) ClueContactQueryList(ctx context.Context, condition map[string]interface{}, pageInfo *utils.PageInfo) (int64, []*GfyxClueContactPo, error) {
	db := c.GetTenantDB(ctx).Table(GfyxClueContactPo{}.TableName())
	if len(condition) > 0 {
		if ids, ok := condition["ids"]; ok {
			db = db.Where("id in (?)", ids)
			delete(condition, "ids")
		}
		if len(condition) > 0 {
			db = db.Where(condition)
		}
	}
	var count int64
	var queryList []*GfyxClueContactPo
	err := db.Count(&count).Error
	if err != nil {
		return 0, nil, err
	}

	if pageInfo != nil && pageInfo.Page > 0 && pageInfo.PageSize > 0 {
		db = db.Offset((pageInfo.Page - 1) * pageInfo.PageSize).Limit(pageInfo.PageSize)
	}
	if err = db.Find(&queryList).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return 0, nil, nil
		}
		return 0, nil, err
	}
	return count, queryList, nil
}

func (c *clueRepo) ClueContactUpdate(ctx context.Context, condition, updates map[string]interface{}) error {
	return c.GetTenantDB(ctx).Table(GfyxClueContactPo{}.TableName()).Where(condition).Updates(updates).Error
}

func (c *clueRepo) ClueContactSave(ctx context.Context, po *GfyxClueContactPo) (*GfyxClueContactPo, error) {
	err := c.GetTenantDB(ctx).Table(po.TableName()).Create(po).Error
	if err != nil {
		return nil, err
	}
	return po, nil
}

func (c *clueRepo) ClueTrackerQueryList(ctx context.Context, condition map[string]interface{}, pageInfo *utils.PageInfo, order string) (int64, []*GfyxClueTrackerPo, error) {
	db := c.GetTenantDB(ctx).Table(GfyxClueTrackerPo{}.TableName()) //.Where(condition)
	if len(condition) > 0 {
		if clueIds, ok := condition["clue_ids"]; ok {
			db = db.Where("clue_id in (?)", clueIds)
			delete(condition, "clue_ids")
		}
		if len(condition) > 0 {
			db = db.Where(condition)
		}
	}

	var count int64
	var queryList []*GfyxClueTrackerPo
	err := db.Count(&count).Error
	if err != nil {
		return 0, nil, err
	}

	if pageInfo != nil && pageInfo.Page > 0 && pageInfo.PageSize > 0 {
		db = db.Offset((pageInfo.Page - 1) * pageInfo.PageSize).Limit(pageInfo.PageSize)
	}
	if order == "" {
		order = "id desc"
	}
	if err = db.Order(order).Find(&queryList).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return 0, nil, nil
		}
		return 0, nil, err
	}
	return count, queryList, nil
}

func (c *clueRepo) ClueTrackerUpdate(ctx context.Context, condition, updates map[string]interface{}) error {
	return c.GetTenantDB(ctx).Table(GfyxClueTrackerPo{}.TableName()).Where(condition).Updates(updates).Error
}

func (c *clueRepo) ClueTrackerSave(ctx context.Context, po *GfyxClueTrackerPo) (*GfyxClueTrackerPo, error) {
	err := c.GetTenantDB(ctx).Table(po.TableName()).Create(po).Error
	return po, err
}

func (c *clueRepo) ClueTrackerFindById(ctx context.Context, id int64) (*GfyxClueTrackerPo, error) {
	detail := GfyxClueTrackerPo{}
	err := c.GetTenantDB(ctx).Table(GfyxClueTrackerPo{}.TableName()).Where("id =? and is_deleted =?", id, constants.IsDeletedFalse).First(&detail).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &detail, nil
}

func (c *clueRepo) ClueTrackerOutboundInChargeUserIdList(ctx context.Context) ([]int64, error) {
	var userIds []int64
	err := c.GetTenantDB(ctx).Table(GfyxClueTrackerPo{}.TableName()+" as a").
		Joins("inner join "+GfyxCluePo{}.TableName()+" as b on a.clue_id = b.id and b.is_deleted = ?", constants.IsDeletedFalse).
		Where("a.is_deleted = ? and a.is_in_charge = ? and b.source = ?",
			constants.IsDeletedFalse, constants.IsInCharge, constants.ClueSourceOutbound).
		Select("distinct(a.user_id) as user_id").
		Pluck("user_id", &userIds).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return userIds, nil
}

func (c *clueRepo) ClueFollowRecordQueryList(ctx context.Context, condition map[string]interface{}, pageInfo *utils.PageInfo) (int64, []*GfyxClueFollowRecordPo, error) {
	db := c.GetTenantDB(ctx).Table(GfyxClueFollowRecordPo{}.TableName()).Where("is_deleted = ?", constants.IsDeletedFalse)
	if len(condition) > 0 {
		if clueIds, ok := condition["clue_ids"]; ok {
			db = db.Where("clue_id in (?)", clueIds)
			delete(condition, "clue_ids")
		}
		if len(condition) > 0 {
			db = db.Where(condition)
		}
	}

	var count int64
	var queryList []*GfyxClueFollowRecordPo
	err := db.Count(&count).Error
	if err != nil {
		return 0, nil, err
	}

	if pageInfo != nil && pageInfo.Page > 0 && pageInfo.PageSize > 0 {
		db = db.Offset((pageInfo.Page - 1) * pageInfo.PageSize).Limit(pageInfo.PageSize)
	}
	if err = db.Order("id desc").Find(&queryList).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return 0, nil, nil
		}
		return 0, nil, err
	}
	return count, queryList, nil
}

func (c *clueRepo) ClueSaveFollowRecord(ctx context.Context, po *GfyxClueFollowRecordPo) (*GfyxClueFollowRecordPo, error) {
	err := c.GetTenantDB(ctx).Table(po.TableName()).Create(po).Error
	return po, err
}

func (c *clueRepo) GetCurrentMonthClueCount(ctx context.Context, userId int64, monthStart string, monthEnd string) (int64, error) {
	var total int64
	err := c.GetTenantDB(ctx).Table(GfyxCluePo{}.TableName()).Where("created_by = ? and created_on >= ? and created_on < ? and is_deleted = ?",
		userId, monthStart, monthEnd, constants.IsDeletedFalse).Count(&total).Error
	if err != nil {
		return 0, err
	}
	return total, nil
}
